package org.tp8.ektelesis.service.impl;

import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.springframework.stereotype.Service;
import org.tp8.ektelesis.listener.CombinationAction;
import org.tp8.ektelesis.listener.KeyCombinationListener;
import org.tp8.ektelesis.service.intf.HookService;

@Service
public class HookServiceImpl implements HookService {

	private final static Logger LOGGER = Logger.getLogger(HookServiceImpl.class
			.toString());

	private Set<KeyCombinationListener> kclSet;

	public HookServiceImpl() {
		kclSet = new HashSet<KeyCombinationListener>();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.tp8.keyhook.service.impl.HookService#applyHook(int[],
	 * org.tp8.keyhook.listener.CombinationAction)
	 */
	@Override
	public void applyHook(int[] combination, CombinationAction action) {
		LOGGER.info("Entering applyHook() : " + combination);
		GlobalScreen screen = getScreen();

		// Create listener
		KeyCombinationListener kcl = new KeyCombinationListener(combination,
				action);
		// Add listener
		screen.addNativeKeyListener(kcl);

		// Keep listener in local set for future actions
		kclSet.add(kcl);

		if (!GlobalScreen.isNativeHookRegistered()) {
			LOGGER.info("Registering native hook");
			try {
				GlobalScreen.registerNativeHook();
			} catch (NativeHookException e) {
				LOGGER.throwing("applyHook", "ERROR", e);
			}
		}

		LOGGER.info("Exiting applyHook()");
	}

	@Override
	public void removeHook(int[] combination) {
		LOGGER.info("Entering removeHook() : " + combination);

		Integer[] keyCodes;
		boolean matches;
		boolean foundMatch;
		// Iterate set of hooked combinations
		for (KeyCombinationListener kcl : kclSet) {
			keyCodes = kcl.getPressedKeys();
			foundMatch = true;
			// Iterate keys of combination
			for (Integer keyCode : keyCodes) {
				matches = false;

				// Iterate passed combination
				for (int combinationKey : combination) {
					// Compare the 2 combinations
					if (keyCode.equals(combinationKey)) {
						matches = true;
						break;
					}
				}
				// If a key does not match, skip the current combination
				if (!matches) {
					foundMatch = false;
					break;
				}
			}
			// If found, unregister
			if (foundMatch) {
				unregister(kcl);
			}
		}


		LOGGER.info("Exiting applyHook()");
	}

	private void unregister(KeyCombinationListener kcl) {
		GlobalScreen screen = getScreen();
		screen.removeNativeKeyListener(kcl);

		if (GlobalScreen.isNativeHookRegistered()) {
			LOGGER.info("Unegistering native hook");
			try {
				GlobalScreen.unregisterNativeHook();
			} catch (NativeHookException e) {
				LOGGER.throwing("applyHook", "ERROR", e);
			}
		}

	}

	private GlobalScreen getScreen() {
		return GlobalScreen.getInstance();
	}
}
